﻿/** Copyright 2010-2012 Twitter, Inc.*/

/**
 * An object that generates IDs.
 * This is broken into a separate class in case
 * we ever want to support multiple worker threads
 * per process
 */

using System;

namespace ZilLion.Service.Core.Snowflake
{
    public class IdWorker
    {
        //public const long Twepoch = 1069142674L;//2000-01-01 时间戳

        protected const int WorkerIdBits = 7;
        protected const int SequenceBits = 10;
        protected const int TimestampBits = 50 - WorkerIdBits - SequenceBits;

        protected const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits);
        protected const long MaxSequence = -1L ^ (-1L << SequenceBits);
        protected const long MaxTimestamp = -1L ^ (-1L << TimestampBits);

        protected const int WorkerIdShift = SequenceBits;
        protected const int TimestampLeftShift = SequenceBits + WorkerIdBits;

        //protected const long SequenceMask = -1L ^ (-1L << SequenceBits);
        
        protected long _sequence = 0L;
        protected long _lastTimestamp = -1L;
        protected long _initTimestamp;
	    
	
        public IdWorker(long workerId,long sequence = 0L) 
        {
            //需要增加WorkID有效性的全工作域检查
            //采用中心登记制或者是中心审批制方式，避免多个端点使用相同的WorkID

            WorkerId = workerId;    
            _sequence = sequence;
            _initTimestamp = TimeGen();
            _lastTimestamp = _initTimestamp;
            // sanity check for workerId
            if (workerId > MaxWorkerId || workerId < 0) 
            {
                throw new ArgumentException(String.Format("内部错误。（WorkID必须在0-{0}范围内|workerId={1}）", MaxWorkerId, workerId) );
            }

            if (_initTimestamp >= MaxTimestamp)
            {
                //当前系统时间超过可支持的最大范围
                throw new InvalidSystemClock(String.Format("系统时间已经超出支持上限。(_initTimestamp ={ 0 }，MaxTimestamp ={ 1})", _initTimestamp, MaxTimestamp));
            }
            //log.info(
            //    String.Format("worker starting. timestamp left shift {0}, datacenter id bits {1}, worker id bits {2}, sequence bits {3}, workerid {4}",
            //                  TimestampLeftShift, DatacenterIdBits, WorkerIdBits, SequenceBits, workerId)
            //    );
        }
	    
        public bool IsReady
        {
            get
            {
                var curTimestamp = TimeGen();
                if (curTimestamp < _initTimestamp)
                {
                    throw new InvalidSystemClock(String.Format("系统时间发生回退。(curTimestamp={0}，_initTimestamp={1})", curTimestamp, _initTimestamp));
                }
                else
                {
                    return (curTimestamp > _initTimestamp);
                }
            }
        }

        public long WorkerId {get; protected set;}
        //public long DatacenterId {get; protected set;}

        public long Sequence
        {
            get { return _sequence; }
            internal set { _sequence = value; }
        }

        // def get_timestamp() = System.currentTimeMillis

        protected readonly object _lock = new Object();
	
        public virtual long NextId() 
        {
            lock(_lock) 
            {
                var timestamp = TimeGen();
                if (timestamp == _initTimestamp)
                {
                    //初始时间戳可能是前一次退出的时间戳，从安全的角度来看应该放弃掉。（而且是建立在系统时间没有后退的前提下）
                    //还有一种回避方法是在New的时候直接等到下一个时间戳
                    throw new InvalidSystemClock(String.Format("需要不超过500ms的初始准备时间。(timestamp ={0}，_initTimestamp ={1})", timestamp, _initTimestamp));
                }

                if (timestamp < _lastTimestamp) 
                {
                    //exceptionCounter.incr(1);
                    //log.Error("clock is moving backwards.  Rejecting requests until %d.", _lastTimestamp);
                    throw new InvalidSystemClock(String.Format("系统时间发生回退。(timestamp={0}，_lastTimestamp={1})", timestamp, _lastTimestamp));
                }

                if (timestamp == _lastTimestamp) 
                {
                    _sequence = _sequence + 1;
                    if (_sequence > MaxSequence) 
                    {
                        //当前时间戳的seq已经用尽
                        throw new InvalidSystemClock(String.Format("当前时间已经不存在可分配资源。(timestamp={0}，_sequence={1}，MaxSequence={2})", timestamp, _sequence, MaxSequence));                        
                    }
                } else {
                    _sequence = 0;
                }

                _lastTimestamp = timestamp;
                var id = (timestamp << TimestampLeftShift) |(WorkerId << WorkerIdShift) | _sequence;
				
                return id;
            }
        }

        protected virtual long TimeGen()
        {
            return System.CurrentTimeMillis();
        }      
    }
}